package com.mars.logger.aspect;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mars.base.common.utils.IpHelper;
import com.mars.base.common.utils.SpringContextHolder;
import com.mars.base.common.utils.date.DateUtil;
import com.mars.base.common.utils.device.UserAgentUtils;
import com.mars.base.common.utils.device.UserAgentVo;
import com.mars.logger.annotation.Log;
import com.mars.logger.dto.SSOUser;
import com.mars.logger.entity.LoggerError;
import com.mars.logger.entity.LoggerLogin;
import com.mars.logger.entity.LoggerOperate;
import com.mars.logger.enums.ActionType;
import com.mars.logger.rpc.api.LoggerErrorService;
import com.mars.logger.rpc.api.LoggerLoginService;
import com.mars.logger.rpc.api.LoggerOperateService;

@Aspect
@Component
public class LogAspect {

	private Logger logger = LoggerFactory.getLogger(LogAspect.class) ;
	
	@Autowired
	private LoggerOperateService logOperateService ;
	
	@Autowired
	private LoggerErrorService logErrorService ;
	
	@Autowired
	private LoggerLoginService logLoginService ;
	
	//当前用户Request Attr
	public static final String REQ_USER_ATTR_NAME = "__curren_sso_user";
	
	private String saveRegEx = "^(add|save|insert)(\\w*|\\W*|\\d*|\\D*)?" ;
	private String deleteRegEx = "^(delete|remove)(\\w*|\\W*|\\d*|\\D*)?" ;
	private String updateRegEx = "^(update|modify)(\\w*|\\W*|\\d*|\\D*)?" ;
	private String listRegEx = "^(read|get|list|find)(\\w*|\\W*|\\d*|\\D*)?" ;
	
	private Pattern compileSave = Pattern.compile(saveRegEx);
	private Pattern compileDelete = Pattern.compile(deleteRegEx);
	private Pattern compileUpdate = Pattern.compile(updateRegEx);
	private Pattern compileList = Pattern.compile(listRegEx);
	
	@Pointcut("execution(public * com.mars.*..*.controller.*.save*(..)) || execution(public * com.mars.*..*.controller.*.insert*(..)) || execution(public * com.mars.*..*.controller.*.add*(..))")
	public void save() {}
	
	@Pointcut("execution(public * com.mars.*..*.controller.*.delete*(..)) || execution(public * com.mars.*..*.controller.*.remove*(..))")
	public void delete() {}
	
	@Pointcut("execution(public * com.mars.*..*.controller.*.update*(..)) || execution(public * com.mars.*..*.controller.*.modify*(..))")
	public void update() {}
	
	@Pointcut("execution(public * com.mars.*..*.controller.*.select*(..)) || execution(public * com.mars.*..*.controller.*.find*(..))")
	public void select() {}
	
	@Pointcut("@annotation(com.mars.logger.annotation.Log)")
	public void toController(){}
	
	@Around("toController() || save() || update() || delete() || select()")
	public Object controllerOperateLog(ProceedingJoinPoint point) throws Throwable {
		Object result = null ;
		Long start = System.currentTimeMillis(), end = 0L, spendTime = 0L ;
		result = point.proceed();	//执行方法所消耗的时间
		end = System.currentTimeMillis();
		spendTime = end - start;
		
		MethodSignature signature = (MethodSignature)point.getSignature() ;
		Log ann = signature.getMethod().getAnnotation(Log.class);
		
		HttpServletRequest request = SpringContextHolder.getRequest() ;
		String actionClass = point.getTarget().getClass().getName();
		String actionMethod = signature.getName() ;
		List<Object> args = Arrays.asList(point.getArgs());
		
		logger.debug("当前操作用户：{}", getCuurentSSOUser());
		
		//操作日志
		if(ann == null || ann.active() && ann.actionType() != ActionType.LOGIN && ann.actionType() != ActionType.LOGOUT) {
			logger.info(
					"操作日志==>操作用户：[{}], 类型：[{}], 描述：{}, 请求IP：{}, 目标对象：{}, 请求URI：{}, 表单参数：{}, 查询参数：{}", 
					getUserName(),
					getActionType(ann, actionMethod).getName(),
					(null != ann?ann.description():""),
					IpHelper.getIpAddr(request), 
					actionClass+"."+actionMethod+"()", 
					request.getRequestURI().toString(),
					(null != args && !args.isEmpty() ? JSON.toJSONString(args) : "无"),
					request.getQueryString()
					);
			
			this.saveOperateLog(point, spendTime.intValue());
		}
		
		//登录日志
		if(null != ann && ann.actionType() == ActionType.LOGIN) {
			JSONObject parseObject = JSON.parseObject(JSON.toJSONString(result)) ;
			Boolean status = (Boolean) parseObject.get("status") ;
			String message = (String) parseObject.get("message") ;
			
			SSOUser user = null ;
			Object data = parseObject.get("data");
			if(null != data) {
				user = JSON.parseObject(data.toString(), SSOUser.class) ;
				if(user != null) {
					user.setStatus(status);
					user.setMessage(message);
				}
			}
			if(null == user) {
				user = new SSOUser() ;
				user.setStatus(status);
				user.setMessage(message);
			}
			logger.info(
					"用户登录=>状态：{}, 提示消息：{}, 姓名：{}, 账号：{}, 请求IP：{}, Token：{}, 登录时间：{}", 
					(user.isStatus()? "成功" : "失败"),
					user.getMessage(),
					user.getRealName(),
					(user.isStatus()? user.getAccount() : args.get(0).toString()),
					IpHelper.getIpAddr(request), 
					user.getToken(),
					DateUtil.getDateTime()
					);
			this.saveLoginLog(point, spendTime.intValue(), user);
		}
		
		//登出日志
		if(null != ann && ann.actionType() == ActionType.LOGOUT) {
			SSOUser user = JSON.parseObject(JSON.toJSONString(result), SSOUser.class) ;
			if(null != user) {
				logger.debug("用户退出登录=>账号：{}, 姓名：{}, 令牌：{}, IP：{}, 登出时间：{}", user.getRealName(), user.getAccount(), user.getToken(), IpHelper.getIpAddr(request), DateUtil.getDateTime());
				this.logLoginService.updateLogoutTime(args.get(0).toString());
			}
		}
		
		return result ;
	}
	
	
	@AfterThrowing(pointcut = "toController() || save() || update() || delete() || select()", throwing = "e")
	public void doAfterThrowing(JoinPoint point, Throwable e) {
		MethodSignature signature = (MethodSignature)point.getSignature() ;
		Log ann = signature.getMethod().getAnnotation(Log.class);
		
		HttpServletRequest request = SpringContextHolder.getRequest() ;
		String actionClass = point.getTarget().getClass().getName();
		String actionMethod = signature.getName() ;
		List<Object> args = Arrays.asList(point.getArgs());
		
		logger.error(
				"异常日志==>操作用户：[{}], 类型：[{}], 请求IP：{}, 对象：{}, 方法：{}, 请求URI：{}, 传入参数：{}", 
				getUserName(),
				getActionType(ann, actionMethod).getName(), 
				IpHelper.getIpAddr(request), 
				actionClass, 
				actionMethod, 
				request.getRequestURI(),
				(null != args && !args.isEmpty() ? JSON.toJSON(args) : "无")
				);
		
		this.saveErrorLog(point, e);
	}
	
	/**
	 * 保存用户登录记录
	 * @param point
	 * @param spendTime
	 * @param user
	 */
	public void saveLoginLog(ProceedingJoinPoint point, int spendTime, SSOUser user) {
		List<Object> args = Arrays.asList(point.getArgs());
		HttpServletRequest request = SpringContextHolder.getRequest() ;
		UserAgentVo userAgent = UserAgentUtils.getUserAgent(request) ;
		
		LoggerLogin entity = new LoggerLogin();
		entity.setLoginAccount((user.isStatus()? user.getAccount() : args.get(0).toString()));
		entity.setLoginStatus((user.isStatus()? 1 : 2));
		entity.setLoginMsg(user.getMessage());
		entity.setLoginTime(new Date());
		entity.setLoginUserId(user.getId());
		entity.setLoginToken(user.getToken());
		entity.setLoginUserName(user.getRealName());
		entity.setLoginIp(IpHelper.getIpAddr(request));
		
		entity.setUserAgent(UserAgentUtils.getUserAgentString(request));
		entity.setTerminalType(userAgent.getTerminalType());
		entity.setPlatformType(userAgent.getPlatformType());
		entity.setPlatformOs(userAgent.getPlatformOs());
		entity.setBrowserType(userAgent.getBrowserType());
		entity.setBrowserVersion(userAgent.getBrowserVersion());
		entity.setBrowserKernel(userAgent.getBrowserKernel());
		
		this.logLoginService.insertSelective(entity) ;
		
	}
	
	/**
	 * 保存用户操作记录
	 * @param point
	 * @param spendTime
	 */
	public void saveOperateLog(ProceedingJoinPoint point, int spendTime) {
		MethodSignature signature = (MethodSignature)point.getSignature() ;
		Log ann = signature.getMethod().getAnnotation(Log.class);
		
		if(ann == null || ann.active()) {
			String actionClass = point.getTarget().getClass().getName();
			String actionMethod = signature.getName() ;
			List<Object> args = Arrays.asList(point.getArgs());
			HttpServletRequest request = SpringContextHolder.getRequest() ;
			UserAgentVo userAgent = UserAgentUtils.getUserAgent(request) ;
			String actionType = getActionType(ann, actionMethod).getName() ;
			
			LoggerOperate entity = new LoggerOperate();
			entity.setActionType(actionType);
			entity.setActionDesc(null != ann && StringUtils.isNotBlank(ann.description()) ? ann.description() : actionType);
			entity.setPermission("");
			entity.setOperatorUser(getUserName());
			entity.setOperatorTime(new Date());
			entity.setSpendTime(spendTime);
			
			entity.setTargetObject(actionClass+"."+actionMethod+"()");
			entity.setFormParams((null != args && !args.isEmpty() ? JSON.toJSONString(args) : "无"));
			entity.setQueryParams(request.getQueryString());
			entity.setRequestIp(IpHelper.getIpAddr(request));
			entity.setRequestMethod(request.getMethod());
			entity.setRequestUri(request.getRequestURI().toString());
			entity.setRequestUrl(request.getRequestURL().toString());
			entity.setUserAgent(UserAgentUtils.getUserAgentString(request));
			entity.setTerminalType(userAgent.getTerminalType());
			entity.setPlatformType(userAgent.getPlatformType());
			entity.setPlatformOs(userAgent.getPlatformOs());
			entity.setBrowserType(userAgent.getBrowserType());
			entity.setBrowserVersion(userAgent.getBrowserVersion());
			entity.setBrowserKernel(userAgent.getBrowserKernel());
			this.logOperateService.insertSelective(entity) ;
		}
	}
	
	public void saveErrorLog(JoinPoint point, Throwable e) {
		
		MethodSignature signature = (MethodSignature)point.getSignature() ;
		Log ann = signature.getMethod().getAnnotation(Log.class);
		
		String actionClass = point.getTarget().getClass().getName();
		String actionMethod = signature.getName() ;
		List<Object> args = Arrays.asList(point.getArgs());
		HttpServletRequest request = SpringContextHolder.getRequest() ;
		UserAgentVo userAgent = UserAgentUtils.getUserAgent(request) ;
		String actionType = getActionType(ann, actionMethod).getName() ;
		
		
		LoggerError entity = new LoggerError() ;
		entity.setActionType(actionType);
		entity.setActionDesc(null != ann && StringUtils.isNotBlank(ann.description()) ? ann.description() : actionType);
		entity.setPermission("");
		entity.setExceptionShort(e.getMessage());
		entity.setExceptionDetail(ExceptionUtils.getRootCauseMessage(e));
		entity.setOperatorUser(getUserName());
		entity.setOperatorTime(new Date());
		
		entity.setTargetObject(actionClass+"."+actionMethod+"()");
		entity.setFormParams((null != args && !args.isEmpty() ? JSON.toJSONString(args) : "无"));
		entity.setQueryParams(request.getQueryString());
		entity.setRequestIp(IpHelper.getIpAddr(request));
		entity.setRequestMethod(request.getMethod());
		entity.setRequestUri(request.getRequestURI().toString());
		entity.setRequestUrl(request.getRequestURL().toString());
		entity.setUserAgent(UserAgentUtils.getUserAgentString(request));
		entity.setTerminalType(userAgent.getTerminalType());
		entity.setPlatformType(userAgent.getPlatformType());
		entity.setPlatformOs(userAgent.getPlatformOs());
		entity.setBrowserType(userAgent.getBrowserType());
		entity.setBrowserVersion(userAgent.getBrowserVersion());
		entity.setBrowserKernel(userAgent.getBrowserKernel());
		
		this.logErrorService.insertSelective(entity) ;
	}
	
	
	/**
	 * 操作类型
	 * @param ann
	 * @param actionMethod
	 * @return
	 */
	public ActionType getActionType(Log ann, String actionMethod) {
		ActionType actionType = null ;
		if(ann != null && StringUtils.isNotBlank(ann.actionType().getName())) {
			actionType = ann.actionType();
		} else {
			
			if(compileSave.matcher(actionMethod).matches()) {
				actionType = ActionType.INSERT;
			} else if(compileUpdate.matcher(actionMethod).matches()) {
				actionType = ActionType.UPDATE;
			} else if(compileDelete.matcher(actionMethod).matches()) {
				actionType = ActionType.DELETE;
			} else if(compileList.matcher(actionMethod).matches()) {
				actionType = ActionType.SELECT;
			} else {
				actionType = ActionType.UNKNOWN;
			}
		}
		
		return actionType;
	}
	
	/**
	 * 获取当前登录用户的信息
	 * 只有登录后才能获取
	 * @return
	 */
	public static SSOUser getCuurentSSOUser() {
		HttpServletRequest request = SpringContextHolder.getRequest() ;
		if(request != null) {
			String userString = JSON.toJSONString(request.getAttribute(REQ_USER_ATTR_NAME)) ;
			if(userString != null) {
				return JSON.parseObject(userString, SSOUser.class) ;
			} else {
				return null ;
			}
		} else {
			return null ;
		}
	}

	/**
	 * 获取当前用户ID
	 * @return
	 */
	public static Integer getUserId() {
		SSOUser cuurentSSOUser = getCuurentSSOUser() ;
		if(cuurentSSOUser != null) {
			return cuurentSSOUser.getId() ;
		} else {
			return null ;
		}
	}

	/**
	 * 获取当前用户账号
	 * @return
	 */
	public static String getUserAccount() {
		SSOUser cuurentSSOUser = getCuurentSSOUser() ;
		if(cuurentSSOUser != null) {
			return cuurentSSOUser.getAccount() ;
		} else {
			return null ;
		}
	}

	/**
	 * 获取当前用户姓名
	 * @return
	 */
	public static String getUserName() {
		SSOUser cuurentSSOUser = getCuurentSSOUser() ;
		if(cuurentSSOUser != null) {
			return cuurentSSOUser.getRealName() ;
		} else {
			return null ;
		}
	}
	
	public static void main(String[] args) {
		String content = "saveinsertaAaddA23";
		//String regEx = "^add\\w*$||^save\\w*$" ;
		String regEx = "^(add|save|insert)(\\w*|\\W*|\\d*|\\D*)?" ;
		Pattern compile = Pattern.compile(regEx);
		Matcher matcher = compile.matcher(content);
		
		System.out.println(matcher.matches());
	}
	
}
